using TerritoryGame.Domain.Entities;
using TerritoryGame.Domain.Events;
using TerritoryGame.Domain.ValueObjects;
using TerritoryGame.Domain.Enums;

namespace TerritoryGame.Domain.Services;

public interface IGameStateService
{
    GameState GetCurrentState(Game game);
    bool CanStartGame(Game game);
    bool CanEndGame(Game game);
    void StartGame(Game game);
    void EndGame(Game game);
    void PauseGame(Game game);
    void ResumeGame(Game game);
    TimeSpan GetRemainingTime(Game game);
    bool IsGameFinished(Game game);
    Player GetWinner(Game game);
}

public class GameStateService : IGameStateService
{
    private readonly Dictionary<Guid, GameState> _gameStates = new();
    private readonly Dictionary<Guid, DateTime> _gameStartTimes = new();
    private readonly Dictionary<Guid, int> _gameDurations = new();
    private readonly object _stateLock = new();

    public GameState GetCurrentState(Game game)
    {
        lock (_stateLock)
        {
            return _gameStates.TryGetValue(game.Id, out var state) ? state : GameState.Waiting;
        }
    }

    public bool CanStartGame(Game game)
    {
        var currentState = GetCurrentState(game);
        return currentState == GameState.Waiting && 
               game.Room.Players.Count >= 2 &&
               game.Room.Players.All(p => p.IsOnline);
    }

    public bool CanEndGame(Game game)
    {
        var currentState = GetCurrentState(game);
        return currentState == GameState.Playing || currentState == GameState.Paused;
    }

    public void StartGame(Game game)
    {
        if (!CanStartGame(game))
            throw new InvalidOperationException("Cannot start game in current state");

        lock (_stateLock)
        {
            _gameStates[game.Id] = GameState.Playing;
            _gameStartTimes[game.Id] = DateTime.UtcNow;
            _gameDurations[game.Id] = game.Room.GameDuration;
        }

        // 重置所有玩家分数
        foreach (var player in game.Room.Players)
        {
            player.ResetScore();
        }
    }

    public void EndGame(Game game)
    {
        if (!CanEndGame(game))
            throw new InvalidOperationException("Cannot end game in current state");

        lock (_stateLock)
        {
            _gameStates[game.Id] = GameState.Finished;
        }
    }

    public void PauseGame(Game game)
    {
        var currentState = GetCurrentState(game);
        if (currentState != GameState.Playing)
            throw new InvalidOperationException("Can only pause a playing game");

        lock (_stateLock)
        {
            _gameStates[game.Id] = GameState.Paused;
        }
    }

    public void ResumeGame(Game game)
    {
        var currentState = GetCurrentState(game);
        if (currentState != GameState.Paused)
            throw new InvalidOperationException("Can only resume a paused game");

        lock (_stateLock)
        {
            _gameStates[game.Id] = GameState.Playing;
        }
    }

    public TimeSpan GetRemainingTime(Game game)
    {
        lock (_stateLock)
        {
            if (!_gameStartTimes.TryGetValue(game.Id, out var startTime) ||
                !_gameDurations.TryGetValue(game.Id, out var duration))
            {
                return TimeSpan.Zero;
            }

            var elapsed = DateTime.UtcNow - startTime;
            var remaining = TimeSpan.FromSeconds(duration) - elapsed;
            return remaining > TimeSpan.Zero ? remaining : TimeSpan.Zero;
        }
    }

    public bool IsGameFinished(Game game)
    {
        var currentState = GetCurrentState(game);
        if (currentState == GameState.Finished)
            return true;

        if (currentState == GameState.Playing)
        {
            var remainingTime = GetRemainingTime(game);
            return remainingTime <= TimeSpan.Zero;
        }

        return false;
    }

    public Player GetWinner(Game game)
    {
        if (!IsGameFinished(game))
            return null;

        var scores = game.CalculateFinalScores();
        var winner = scores.OrderByDescending(kvp => kvp.Value.Area).First();
        
        return game.Room.Players.FirstOrDefault(p => p.Id == winner.Key);
    }
} 